package com.gitee.apanlh.util.base;

import com.gitee.apanlh.util.cache.local.Cache;
import com.gitee.apanlh.util.cache.local.CacheUtils;
import com.gitee.apanlh.util.reflection.ClassUtils;
import com.gitee.apanlh.util.reflection.ReflectionUtils;
import com.gitee.apanlh.util.valid.ValidParam;

import java.lang.reflect.Constructor;

/**	
 * 	单例工具类
 * 	
 * 	@author Pan
 */
public class SingletonUtils {
	
	static final Cache<String, Object> SINGLETOM_CACHE = CacheUtils.cache(64);
	
	/**
	 * 	默认构造函数
	 * 
	 * 	@author Pan
	 */
	private SingletonUtils() {
		//	不允许外部实例
		super();
	}
	
	/**
	 * 	获取单例
	 * 	<br>空构造函数 Key: ClassName 
	 * 	<br>构造函数     Key: ClassName + @ + 参数值(多个值#分割)
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      	数据类型
	 * 	@param 	t			Class
	 * 	@param 	parameters	构造函数参数
	 * 	@return	T
	 */
	public static <T> T get(Class<T> t, Object... parameters) {
		String key = getCacheKey(t, parameters);
		
		if (ValidParam.isEmpty(parameters)) {
			//	获取空构造函数
			return getInstance(key, true, ReflectionUtils.getConstructor(t), null);
		} 
		
		//	获取参数的Class
		Class<?>[] paramsClasses = ClassUtils.getClasses(parameters);
		//	获取和参数匹配的构造函数
		return getInstance(key, false, ReflectionUtils.getConstructor(t, paramsClasses), parameters);
	}
	
	/**
	 * 	获取缓存检查
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      数据类型
	 * 	@param 	key					键
	 * 	@param 	isEmptyConstructor	是否空构造函数
	 * 	@param 	constructor			构造函数	
	 * 	@param 	parameters			参数
	 * 	@return T
	 */
	@SuppressWarnings("unchecked")
	private static <T> T getInstance(String key, boolean isEmptyConstructor, Constructor<T> constructor, Object[] parameters) {
		return (T) SINGLETOM_CACHE.get(key, () -> isEmptyConstructor ? constructor.newInstance() : constructor.newInstance(parameters));
	}
	
	/**	
	 * 	默认Key为ClassName + @ + 参数值(多个值#分割)
	 * 	
	 * 	@author Pan
	 * 	@param  <T>      		数据类型
	 * 	@param 	clazz			类
	 * 	@param 	parameters		参数
	 * 	@return String
	 */
	private static <T> String getCacheKey(Class<T> clazz, Object... parameters) {
		//	空构造函数
		if (ValidParam.isEmpty(parameters)) {
			return clazz.getName();
		}
		return StringUtils.format("{}@{}", ClassUtils.getName(clazz), ArrayUtils.join(parameters, "#"));
	}
}
